# -*- coding: utf-8 -*-
import torch
import numpy as np
from datasets import load_dataset
from peft import get_peft_model, LoraConfig, TaskType
from transformers import BertForSequenceClassification, BertTokenizer, Trainer, TrainingArguments
from sklearn.metrics import accuracy_score, precision_recall_fscore_support

# 设备配置
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")


# 加载本地BERT模型
model_dir = '/root/autodl-tmp/models/pretrained/google-bert/bert-base-chinese'
model = BertForSequenceClassification.from_pretrained(model_dir, num_labels=2).to(device)

# LoRA配置
lora_config = LoraConfig(
    task_type=TaskType.SEQ_CLS,
    r=8,
    lora_alpha=32,
    target_modules=["query", "key", "value"],
    lora_dropout=0.1,
    bias="none"
)



# 应用LoRA
model = get_peft_model(model, lora_config)

# 输出可训练参数数量
trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f"可训练参数总数: {trainable_params}")


# 加载本地数据集并裁剪样本
dataset = load_dataset("/root/autodl-tmp//data/raw/ChnSentiCorp")
# 选择部分参数测试一下:
dataset['train'] = dataset['train'].select(range(500))
dataset['test'] = dataset['test'].select(range(200))



# 加载分词器
tokenizer = BertTokenizer.from_pretrained(model_dir)
# 分词函数
def tokenize(batch):
    return tokenizer(batch['text'], padding='max_length', truncation=True, max_length=128)
# 数据集分词
tokenized_datasets = dataset.map(tokenize, batched=True)
tokenized_datasets.set_format(type='torch', columns=['input_ids', 'attention_mask', 'label'])

# 计算评估指标
def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    precision, recall, f1, _ = precision_recall_fscore_support(labels, predictions, average='binary')
    acc = accuracy_score(labels, predictions)
    return {"accuracy": acc, "f1": f1, "precision": precision, "recall": recall}



# 训练参数
training_args = TrainingArguments(
    output_dir='/root/autodl-tmp/models/fine_tuned/lora_results',
    num_train_epochs=3,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=16,
    evaluation_strategy="steps",
    eval_steps=100,
    save_steps=100,
    logging_steps=50,
    learning_rate=2e-4,
    weight_decay=0.01,
    save_total_limit=1,
    load_best_model_at_end=True,
    metric_for_best_model="accuracy"
)

# 初始化Trainer
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_datasets['train'],
    eval_dataset=tokenized_datasets['test'],
    tokenizer=tokenizer,
    compute_metrics=compute_metrics
)

# 开始训练
trainer.train()
